package week6;

import java.util.LinkedList;

import tookit.Debug;

public class ChainingHashDemo<E>
{
	LinkedList<E>[] table;
	
	private int length;
	
	public ChainingHashDemo()
	{
		this(5);
	}
	
	@SuppressWarnings("unchecked")
	public ChainingHashDemo(int length)
	{
		this.table = new LinkedList[length];
		
		this.length = length;
		
		this.populate();
	}
	
	public boolean search(E e)
	{
		int position = this.getPosition(e);
		
		return (this.table[position] != null
			? false
			: this.table[position].contains(e)
		);
	}
	
	private int getPosition(E e)
	{
		int current = e.hashCode() % this.length;
		
		if (current < 0)
		{
			current=current+this.length;
			//throw new RuntimeException("Invalid element (cannot be negative values).");
		}
		
		return current;
	}
	
	private int getTableCount()
	{
		int count = 0;
		
		for (LinkedList<E> tableRow: this.table)
		{
			count += tableRow.size();
		}
		
		return count;
	}
	
	public int getLength()
	{
		return this.length;
	}
	
	private void populate()
	{
		for (int i = 0; i < this.length; i++)
		{
			table[i] = new LinkedList<E>();
		}
	}
	
	public boolean insert(E e)
	{
		if (this.getTableCount() > 0.75 * (this.length - 1))
		{
			this.rehash();
		}
		
		int position = this.getPosition(e);
		
		if (!this.table[position].contains(e))
		{
			this.table[position].add(e);
			
			return true;
		}

		return false;
	}
	
	public boolean delete(E e)
	{
		int position = this.getPosition(e);
		
		if (this.table[position].contains(e) && this.table[position].remove(e))
		{
			return true;
		}
		
		return false;
	}
	
	@SuppressWarnings("unchecked")
	private void rehash()
	{
		LinkedList<E>[] backup = this.table;
		
		this.length = 2 * this.length;
		
		this.table = new LinkedList[this.length];
		
		this.populate();
		
		for (LinkedList<E> tableRow: backup)
		{
			for (E e: tableRow)
			{
				this.table[this.getPosition(e)].add(e);
			}
		}
		Debug.printSolidLine();
		System.out.println(this.toString());
		System.out.println("Table count: " + this.getTableCount());
		System.out.println("Rehash when > " + (this.getLength() * 0.75));
	}
	
	public String toString()
	{
		String output = "";
		
		int position = 0;
		
		for (LinkedList<E> tableRow: this.table)
		{
			output += position + " ";
			
			for (E e: tableRow)
			{
				output += "[" + e + "] --> ";
			}
			
			output += "\n";
			
			position++;
		}
		
		return output;
	}
	
	public static void main(String[] s)
	{
		
		ChainingHashDemo<Double> chd = new ChainingHashDemo<Double>();
		
		for(int i=0;i<100;i++)
		{
			chd.insert((double)Math.random()*100);
		}
	

		Debug.printSolidLine();
		Debug.log("The result:");
		System.out.println(chd.toString());
		System.out.println("Table count: " + chd.getTableCount());
		System.out.println("Rehash when > " + (chd.getLength() * 0.75));
		
		
		
	}
}
